语言模型演进:N-gram 到 RNN
语言模型(Language Model, LM)的任务是计算一个词序列出现的概率。这条演进线要解决的问题始终是同一个:上下文能看多长,以及没见过的情况怎么办。
N-gram 与马尔可夫假设
一个句子的概率按概率的链式法则展开:
直接算不现实:像
马尔可夫假设的作用是把它砍短——一个词的出现只与前面有限的
- Bigram(N=2):
- Trigram(N=3):
概率用**最大似然估计(MLE)**从语料里数出来,思想就是「数据里出现次数最多的最可能」:
手算例子。语料 datawhale agent learns, datawhale agent works(共 6 个词),估 datawhale agent learns 的 Bigram 概率:
| 步骤 | 计算 | 结果 |
|---|---|---|
| 0.333 | ||
| 1.000 | ||
| 0.500 | ||
| 连乘 | 0.167 |
两个致命缺陷
- 数据稀疏性(Sparsity):词序列没在语料里出现过,概率估计就是 0。平滑(Smoothing)能缓解,但根除不了。
- 泛化能力差:模型不理解词与词之间的语义相似性。见过很多次
agent learns,也泛化不到robot learns——如果robot或robot learns没出现过,概率同样是 0,因为模型不知道agent和robot语义相近。
根因是 N-gram 把词当成孤立、离散的符号——它统计的是离散符号之间的条件概率,即使两个词语义极近也无法在同一上下文里互相替代,这在相当程度上加剧了数据稀疏。
平滑的两条路线
| 方法 | 做法 |
|---|---|
| 拉普拉斯加一 | 最简单的做法——给所有计数都加 1,保证没有零概率。代价是把大量概率质量分给了从未出现过的组合 |
| Kneser-Ney 平滑 | 改进型回退平滑,用接续概率(一个词作为「延续」出现的可能性)而非原始频次来估计低阶分布。至今仍是 N-gram 的强基线 |
「平滑」这个方向的本质是承认一件事:语料的观测计数不可靠,需要给未观测事件留概率质量。后来的神经语言模型换了一条路——不修计数,而是把词变成连续向量。
语言模型的经典评价指标:困惑度
在讲怎么改进之前,得先说清「怎么衡量好坏」。困惑度(Perplexity, PPL) 是这一层最标准的指标。
定义
困惑度就是交叉熵的指数(用自然对数时
等价的几何平均形式更直观:
怎么解读
| 情形 | 困惑度 |
|---|---|
| 完美预测 | 1 |
| 给每个正确 token 都分配 0.25 的概率 | 4(因为 |
| 在 V 个 token 上均匀预测 | V |
「有效选择数」这个说法要当直觉用,别当定义:困惑度 4 不意味着每个位置都恰好有 4 个等概率候选——真实分布在不同位置差异很大,困惑度把它们平均掉了。
量级参照(这条最有价值)
| 模型 / 基准 | 困惑度 |
|---|---|
| Elman RNN(1990),PTB | 55 |
| LSTM(2002),PTB | 43 |
| Bigram(2 万词表) | ~200 |
| Trigram | ~100–150 |
| 现代神经语言模型,PTB | < 20 |
| GPT-2,WikiText-103 | 18.3 |
| GPT-2,BooksCorpus | 20.2 |
| GPT-3,BooksCorpus | 9.8 |
| 中文文本(一般范围) | 50–1000 |
这张表就是「神经模型相对统计模型」的量级差:PTB 上从 55 降到 20 以下。
另一条参照是理论上限:香农估计英语的熵率约 1.0–1.3 bits/字符,对应理论最低字符级困惑度约 2–2.5。实际模型的困惑度必然高于语言的真熵,因为模型只是不完美的近似。
困惑度的数值要放在同一坐标系里看才有意义。同一个测试集上跨代际的量级差:
困惑度(箭头右侧是配套的模型与基准;条形长度按下表数值取对数,只用于看量级)
≈200 ┤ ████████████████████ Bigram(V = 2 万)
≈100–150┤ ███████████████ Trigram
55 ┤ ██████ Elman RNN,PTB(1990)
43 ┤ ████ LSTM,PTB(2002)
<20 ┤ ██ 现代神经语言模型,PTB
18.3 ┤ ██ GPT-2,WikiText-103
9.8 ┤ █ GPT-3,BooksCorpus
│
1.0 ┼─── 完美预测
│
└──── 理论下界:香农给出的英语熵率约 1.0–1.3 bits/字符,
对应字符级困惑度约 2–2.5。实际模型必然高于它 —— 模型只是真熵的不完美近似
另外三个锚点:给正确 token 各分 0.25 概率 → 4;在 V 个 token 上均匀预测 → V比较的前提:词表必须一致
只有在相同的词表和测试集下比较困惑度才有意义。
原因直白:不同词表意味着不同的有效分支因子。V=50000 的词表和一个 V=150000 的词表,「均匀预测」时的困惑度本来就差 3 倍——这两个数字根本不在同一个坐标系里。
当词表确实不同时,用 bits per byte 或 bits per character 做归一化更可比。
计算困惑度时必须对齐的六件事
这部分很实用——同样的模型和测试集,预处理不同就能得到不同的困惑度:
| 项 | 要求 |
|---|---|
| Tokenizer | 不同 tokenizer 把同一文本切成不同目标,token 级困惑度不可直接比较 |
| 语料与预处理 | 文档边界、归一化、end-of-text 标记、包含哪些文本,都会影响结果 |
| 上下文策略 | 窗口长度、stride、上下文是否在文档或块边界重置 |
| 损失掩膜 | padding 和刻意排除的 prompt 位置不应计入损失,也不应计入 token 数 |
| 模型模式 | dropout 必须关闭 |
| 采样设置 | 必须用模型的原始下一个 token 概率,不能经过采样、top-k 过滤或温度改动(见 04-采样参数) |
另外要区分训练困惑度和验证困惑度:训练困惑度会随着模型记住训练样本而一直下降;验证困惑度测的是对未见文本的泛化,通常是更有信息量的那个数。
困惑度的边界
它不直接测量事实准确性、推理、指令遵循、安全性或完整生成回答的质量。
- 模型可以对常见文本模式给出高似然,同时给出错误答案
- 指令调优的模型可以变得更有用,而在无关的预训练语料上困惑度并不改善——这是内在指标与外在表现的经典脱节
- 困惑度隐藏了平均值内部的差异:少数领域或 token 类型可能有极高的损失,而总体数字看着还算合理 → 要配合逐 token 损失和分领域结果一起看
这也解释了为什么今天谈模型能力不再用困惑度:它是「训练曲线和同条件消融」的好指标,但选型要配合任务特定检查(见 08-模型选型 与 LLM Evaluation 与反馈闭环)。
神经网络语言模型与词嵌入
2003 年 Bengio 等人提出的前馈神经网络语言模型是这条线的里程碑(Bengio et al., A neural probabilistic language model, JMLR 2003)。它的建模公式和 N-gram 一样(仍然只看前
结构分三层:
| 层 | 做什么 |
|---|---|
| 输入层 | 通过词嵌入矩阵 |
| 隐藏层 | 非线性变换 |
| 输出层 | 输出下一个词的概率分布 |
词嵌入是这个架构解决稀疏性的关键:语义相近的词,向量位置也相近(agent 与 robot 靠得近,agent 与 apple 离得远)——于是即使 robot learns 从未出现,模型也能从 agent learns 泛化过去。
词嵌入是训练过程中自动学出来的 —— 在「预测下一个词」这个任务里,模型为了降低预测误差会不断调整每个词的向量位置,最终让向量蕴含语义。
衡量向量关系用余弦相似度:
方向相同 → 余弦 1(完全相关);正交 → 0(毫无关系);相反 → −1(完全负相关)。
词向量能捕捉抽象关系,经典例子:
相当于在语义空间里做平移:「国王」减去「男性」加上「女性」落到「女王」的位置。
从 Bengio 到 Word2Vec
Bengio 的做法把「学词向量」当作语言模型的副产品——训练成本高。后续工作把它拆出来当成独立目标:
| 方法 | 思路 |
|---|---|
Word2Vec(Mikolov et al., 2013) | 两个变体:CBOW(用上下文词预测中心词)和 Skip-gram(用中心词预测上下文词)。用负采样或层次 softmax 把训练成本压下来——不再需要对整个词表做归一化 |
| GloVe | 走全局共现统计路线:先统计整个语料的词共现矩阵,再对它做矩阵分解——把「局部窗口预测」和「全局统计」结合起来 |
| FastText | 在词向量之上额外学子词(字符 n-gram)向量,把词表示成子词向量之和 → 能处理未登录词:即使一个词整体没见过,它的子词片段见过 |
这三条路线的分野很清晰:Word2Vec 是局部窗口 + 预测,GloVe 是全局共现 + 分解,FastText 是在词的基础上加子词结构。这个分层在 BPE 那里也能看到回声——子词是解决未登录词的通用手段。
共同的局限:上下文窗口仍然是固定的(只能看前 apple 在「吃苹果」和「苹果公司」里是同一个向量。
Bengio 2003 的前馈网络语言模型:窗口仍是前 n−1 个词,换掉的是词的表示。
前 n−1 个词的 id
│
▼
词嵌入矩阵 C ∈ R^{|V| × m} ── 按 id 取行,每一行就是一个词的向量
│
▼
拼接(n = 3 时把 2 个 m 维向量首尾接成 2m 维)
│
▼
隐藏层:非线性变换
│
▼
输出层 ──▶ 下一个词在整词表上的分布泛化能力从第一条边开始:连续向量让语义相近的词在空间里相邻,所以 robot learns 没在语料里出现过,也能从 agent learns 迁移过去。
Bengio 把学词向量当作语言模型的副产品,训练成本高;后续三条路线把它拆出来当成独立目标。
Word2Vec ── 局部窗口 + 预测 ─┬─ CBOW:用上下文词预测中心词
└─ Skip-gram:用中心词预测上下文词
成本靠负采样或层次 softmax 压下来
GloVe ── 全局共现 + 分解 ── 先统计整个语料的词共现矩阵,再对它做矩阵分解
FastText ── 词 + 子词结构 ── 额外学子词(字符 n-gram)向量,词表示成子词向量之和
未登录词也能表示:整体没见过,它的片段见过
共同的局限:窗口仍然固定(只看前 n−1 个词),而且一个词只有一个静态向量
—— 「吃苹果」和「苹果公司」里的 apple 是同一个向量RNN 与 LSTM
RNN 打破了固定窗口:引入**隐藏状态(hidden state)**向量充当短期记忆,每一步读入当前词、结合上一刻的隐藏状态,生成新的隐藏状态传给下一刻。信息因此在序列里不断向后传递。
这里解决了两个 N-gram 的老问题:窗口不再固定(理论上能看到序列开头),参数也不再随窗口线性增长(同一组权重在每一步复用)。
长期依赖问题:RNN 的序列长度就是网络深度,梯度反向传播要经过多次连乘,会快速趋向零(梯度消失)或变得极大(梯度爆炸)。梯度消失使模型学不到序列早期信息对后期输出的影响。
LSTM(Hochreiter & Schmidhuber, Long short-term memory, Neural Computation 1997)是对这个问题的解答。
RNN 的隐藏状态打破了固定窗口。把同一组权重在时间轴上展开看,链条与瓶颈都在同一条线上。
RNN 按时间展开(每一步用同一组 W_x / W_h / b)
x1 x2 x3 x4
│ │ │ │
▼ ▼ ▼ ▼
┌────┐ ┌────┐ ┌────┐ ┌────┐
│ h1 │──▶│ h2 │──▶│ h3 │──▶│ h4 │──▶ …
└────┘ └────┘ └────┘ └────┘
窗口不再固定:h_t 理论上带着一路传下来的全部历史
参数也不随窗口增长:每步复用的都是同一组权重
瓶颈也在同一条链上,三条同时受限
① 计算:第 t 步必须等第 t−1 步 ──▶ 序列多长就串行多少步
② 梯度:反向传播沿这条链连乘 t 次 ──▶ 衰减或爆炸
③ 容量:整个历史压进固定维度的 h_t ──▶ 序列越长丢得越多门控的机制
核心是细胞状态(Cell State)
| 组件 | 作用 |
|---|---|
| 细胞状态 | 一条独立于隐藏状态的信息通路,让信息在时间步之间更顺畅地传递 |
| 遗忘门 | 决定从上一时刻的细胞状态里丢弃哪些信息 |
| 输入门 | 决定把当前输入中的哪些新信息存入细胞状态 |
| 输出门 | 决定根据当前细胞状态输出哪些信息到隐藏状态 |
缓解梯度消失的关键在那条
GRU 是后来的简化版本:把遗忘门和输入门合并成一个「更新门」,并去掉独立的细胞状态(用隐藏状态兼任),只保留更新门 + 重置门。参数更少、训练更快,在很多任务上与 LSTM 持平。
LSTM 的机制可以压缩成一张数据流图:一条加法通路加三扇门。
LSTM 单元:一条加法通路 + 三扇门
C_{t−1}(上一刻的细胞状态)
│
├──────────────────────────────┐
│ │
▼ │
⊙ 遗忘门 f_t ──────────────────────────┤ 丢弃哪些旧信息
│
⊙ 输入门 i_t ⊗ 候选 C_cand ─────────────┤ 存入哪些新信息
│
▼
C_t = f_t ⊙ C_{t−1} + i_t ⊙ C_cand ← 逐元素加权相加
│
▼
⊙ 输出门 o_t ──▶ h_t = o_t ⊙ tanh(C_t) ← 交给下一步与下一层三个门都是 sigmoid 输出,取值落在 (0, 1)——「门」的含义就是控制信息通过的比重,趋近 0 相当于全丢,趋近 1 相当于全留。
梯度沿哪条路回传,决定了梯度消失有没有被缓解:
GRU 是简化版本:遗忘门与输入门合并成一个更新门,去掉独立的细胞状态,只留更新门与重置门。参数更少,很多任务上与 LSTM 持平。
RNN/LSTM 的瓶颈
计算必须按顺序进行,第
这一条限制同时压住了三件事:
| 影响 | 说明 |
|---|---|
| 无法大规模并行 | 训练时无法把整个序列摊到多个设备上算 |
| 长序列效率低 | 序列多长就要串行多少步 |
| 隐状态是固定长度的瓶颈 | 整个历史必须压进一个固定维度的向量——序列越长,损失越多 |
第三个限制最根本,而且它不随门控改进而消失:LSTM 让信息传得更远,但**「所有历史压进定长向量」这个信息瓶颈没解决**。
这正是 02-Transformer 架构 要解决的问题:用注意力让任意两个位置直接相连,既绕开定长瓶颈,又把串行计算换成并行计算。
相关
- 02-Transformer 架构 —— 用注意力替换循环结构,换来并行计算
- 05-文本分词与子词算法:BPE、WordPiece 与 Unigram —— 从「词」到「子词」,是同一个「如何处理未见单元」问题的另一条解法
- 04-采样参数 —— 现代语言模型的解码在这一层之后
参考
- 《Hello-Agents》第三章 §3.1.1
- Bengio, Y., et al. A neural probabilistic language model. JMLR, 2003.
- Elman, J. L. Finding structure in time. Cognitive Science, 1990.
- Hochreiter, S., & Schmidhuber, J. Long short-term memory. Neural Computation, 1997.
- https://sebastianraschka.com/faq/docs/perplexity-what-it-means.html 、https://computationallinguistics.com/Perplexity
- https://www.aiglossary.xyz/glossary/perplexity
- https://link.springer.com/content/pdf/10.1007/978-981-92-0682-7_5
YJ